#include <stdio.h>
#include "stdafx.h"
#include "Solver.h"
#include "Settings.h"

Solver :: Solver(Configurator* configurator)
{
	dwarfConfigurator = configurator;					// process settings from the command line 
	dwarfConfigurator -> WriteSettings();
	dwarfConfigurator -> GetContent(&mainContent, &contentSize);
}

void Solver::InitParallelization(int threadCount)
{
    int interval = 1 + contentSize / threadCount;   // partitioning the String content (same parts for all threads)
    int leftBoard = 0;
    int part = 0;									// iteration counter for parts of content
	threadBounds[0] = 0;
	threadBounds[threadCount] = contentSize;
    while(part < threadCount - 1)
    {
		int certainInterval = interval;
        while (mainContent[leftBoard + certainInterval] != ' ')	// search of middle-word spaces
        {
			certainInterval++;
        }
		certainInterval++;
        leftBoard += certainInterval;						// interval bounds defifition
		threadBounds[part + 1] = leftBoard;
		part++;
    }
}

void Solver::SumThreadResult(map<string, int> &partialStringTotal)	// add partialStringTotal pair values to class-member stringTotal pair values
{
	for (map<string, int>::const_iterator it = partialStringTotal.begin(); it != partialStringTotal.end(); ++it)
	{
		string partialKey = it -> first;
		int partialValue = it -> second;
		for (map<string, int>::const_iterator ittotal = stringTotal.begin(); ittotal != stringTotal.end(); ++ittotal)
		{
			if ((ittotal -> first) == partialKey)			// needs to be refreshed 
			{
				partialValue += ittotal -> second;			// updating Dictionary values
				break;										// entry finded
			}
		}
		stringTotal[partialKey] = partialValue;				// refresh
	}
	partialStringTotal.clear();					// clearing the unused memory (all data is already stringTotal)
}

	// Runs method Map and method Reduce serially for all process and sum the results
void Solver::Solve()
{
	threadBounds = (int*) malloc (sizeof(int)*(dwarfConfigurator -> GetThreadCount() + 1));
	InitParallelization(dwarfConfigurator -> GetThreadCount());  // code partitioning

		map<string, int> partialStringTotal;
		list<string> partialStringEntries;

	omp_lock_t lk;
	omp_init_lock( &lk );

	#pragma omp parallel for private(partialStringTotal, partialStringEntries)					// openMP directive for parallelization of 'threadCount' parts of code
	for (int i = 0; i < dwarfConfigurator -> GetThreadCount(); i++)
	{	
		Map(partialStringEntries, i);
		Reduce(partialStringTotal, partialStringEntries);
		omp_set_lock( &lk );									// synhronization omp_lock_t
		SumThreadResult(partialStringTotal);	
		omp_unset_lock( &lk );
	}
	omp_destroy_lock( &lk );
	free(mainContent);		// clearing the unused memory (all data is already consisted in stringEntries)
}

string Solver::TransformCharFragment(int begin, int end)
{
	int length = end - begin;
	char* currentWord = (char*) malloc (sizeof(char)*length);				// dynamic memory allocation for new char array
	for (int j = 0; j < length; j++)
	{
		currentWord[j] = mainContent[begin + j];							// filling the array
	}
	string transform(reinterpret_cast<const char*>(currentWord), length);	// convert to string
	free(currentWord);														// free memory from char array
	return transform;
}

// Traverses the string 'content'. Collect each word as substring of 'content'. Change the 'stringEntries' value
void Solver::Map(list<string> &partialStringEntries, int threadNumber)
{
	int i = threadBounds[threadNumber];											// iteration counter
    int count = 0;						// local variable for storing the length of each word
	while (i < threadBounds[threadNumber + 1])
	{
		if ((mainContent[i] > 47 && mainContent[i] < 58)  ||
            (mainContent[i] > 96 && mainContent[i] < 123) ||
            (mainContent[i] > 64 && mainContent[i] < 91)  ||
             mainContent[i] == 95						  ||
             mainContent[i] == 45					      ||
             mainContent[i] == 39)
		{
			count++;														// getting the length of a string
        }
        else
        {
			if (count > 0)
			{
				string stringFragment = TransformCharFragment(i - count, i);
				partialStringEntries.push_back(stringFragment);					// add substring
            }
			string stringFragment = TransformCharFragment(i, i + 1);
			partialStringEntries.push_back(stringFragment);						// add single-char string
            count = 0;														// start length counting of a new word
        }
        i++;
    }
} 

//Groups 'stringEntries' elements and count the number of entry of each word
void Solver::Reduce(map<string, int> &partialStringTotal, list<string> &partialStringEntries)
{
	partialStringEntries.sort();
    int count = 1;										// local variable for storing the count of each word
	string strCur;
	string strPre = partialStringEntries.front();
	partialStringEntries.pop_front();	
	while (!partialStringEntries.empty())				// traverse the list	
    {
		strCur = partialStringEntries.front();
		partialStringEntries.pop_front();										
		if (strCur == strPre)							// checking the equivalence of adjoining words
		{
			count++;                                    // getting count of the word
        }
		else
        {
			partialStringTotal[strPre] = count;				// add to the final list
            count = 1;									// start the next word's count
        }
		strPre = strCur;
    }
	stringTotal[strPre] = count;				// add to the final list
	partialStringEntries.clear();			// clearing the unused memory (all entrines is already consisted in stringTotal)
}

void Solver::Finish(double time)	// Problem results output
{
	dwarfConfigurator -> Close(time, stringTotal);
	stringTotal.clear();			// clearing the unused memory (all data is already printed)
}